// Copyright 2020 Joan Pablo Jimenez Milian. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle;

import 'package:bruno/bruno.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:reactive_forms/reactive_forms.dart';
import 'package:sprintf/sprintf.dart';
import 'package:yt_devops/components/yt_form/utils/form_widget_utils.dart';
import 'package:yt_devops/pages/common/select_page/select_page_binding.dart';
import 'package:yt_devops/pages/common/select_page/select_page_model.dart';
import 'package:yt_devops/pages/common/select_page/select_page_view.dart';
import 'package:yt_devops/resources/str_res_keys.dart';
import 'package:yt_devops/routers/router_path.dart';
import 'package:yt_devops/styles/styles.dart';
import 'package:yt_devops/utils/brn_row1_delegate.dart';

/// A [ReactiveTextSelect] that contains a [TextField].
///
/// This is a convenience widget that wraps a [TextField] widget in a
/// [ReactiveTextSelect].
///
/// A [ReactiveForm] ancestor is required.
///
class ReactiveTextSelect<ModelDataType, ViewDataType>
    extends ReactiveFormField<ModelDataType, ViewDataType> {
  final TextEditingController? _textController;

  /// Creates a [ReactiveTextSelect] that contains a [TextField].
  ///
  /// Can optionally provide a [formControl] to bind this widget to a control.
  ///
  /// Can optionally provide a [formControlName] to bind this FormReactiveTextField
  /// to a [FormControl].
  ///
  /// Must provide one of the arguments [formControl] or a [formControlName],
  /// but not both at the same time.
  ///
  /// Can optionally provide a [validationMessages] argument to customize a
  /// message for different kinds of validation errors.
  ///
  /// Can optionally provide a [valueAccessor] to set a custom value accessors.
  /// See [ControlValueAccessor].
  ///
  /// Can optionally provide a [showErrors] function to customize when to show
  /// validation messages. Reactive Widgets make validation messages visible
  /// when the control is INVALID and TOUCHED, this behavior can be customized
  /// in the [showErrors] function.
  ///
  /// ### Example:
  /// Binds a text field.
  /// ```
  /// final form = fb.group({'email': Validators.required});
  ///
  /// ReactiveTextSelect(
  ///   formControlName: 'email',
  /// ),
  ///
  /// ```
  ///
  /// Binds a text field directly with a *FormControl*.
  /// ```
  /// final form = fb.group({'email': Validators.required});
  ///
  /// ReactiveTextSelect(
  ///   formControl: form.control('email'),
  /// ),
  ///
  /// ```
  ///
  /// Customize validation messages
  /// ```dart
  /// ReactiveTextSelect(
  ///   formControlName: 'email',
  ///   validationMessages: {
  ///     ValidationMessage.required: 'The email must not be empty',
  ///     ValidationMessage.email: 'The email must be a valid email',
  ///   }
  /// ),
  /// ```
  ///
  /// Customize when to show up validation messages.
  /// ```dart
  /// ReactiveTextSelect(
  ///   formControlName: 'email',
  ///   showErrors: (control) => control.invalid && control.touched && control.dirty,
  /// ),
  /// ```
  ///
  /// For documentation about the various parameters, see the [TextField] class
  /// and [TextField], the constructor.
  ReactiveTextSelect({
    Key? key,
    String? formControlName,
    FormControl<ModelDataType>? formControl,
    Map<String, ValidationMessageFunction>? validationMessages,
    ControlValueAccessor<ModelDataType, ViewDataType>? valueAccessor,
    ShowErrorsFunction<ModelDataType>? showErrors,
    bool requireStar = false,
    // 左侧标题
    String title = '',
    // 左侧标题的宽
    double titleWidth = 150,
    InputDecoration decoration = const InputDecoration(),
    TextInputType? keyboardType,
    TextCapitalization textCapitalization = TextCapitalization.none,
    TextInputAction? textInputAction,
    TextStyle? style,
    TextStyle? labelStyle,
    StrutStyle? strutStyle,
    TextDirection? textDirection,
    TextAlign textAlign = TextAlign.end,
    TextAlignVertical? textAlignVertical = TextAlignVertical.center,
    bool autofocus = false,
    bool enable = false,
    ToolbarOptions? toolbarOptions,
    bool? showCursor,
    bool obscureText = false,
    String obscuringCharacter = '•',
    bool autocorrect = true,
    SmartDashesType? smartDashesType,
    SmartQuotesType? smartQuotesType,
    bool enableSuggestions = true,
    MaxLengthEnforcement? maxLengthEnforcement,
    int? maxLines = 1,
    int? minLines,
    bool expands = false,
    int? maxLength,
    List<TextInputFormatter>? inputFormatters,
    double cursorWidth = 2.0,
    double? cursorHeight,
    Radius? cursorRadius,
    Color? cursorColor,
    Brightness? keyboardAppearance,
    EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
    bool enableInteractiveSelection = true,
    InputCounterWidgetBuilder? buildCounter,
    ScrollPhysics? scrollPhysics,
    FocusNode? focusNode,
    Iterable<String>? autofillHints,
    MouseCursor? mouseCursor,
    DragStartBehavior dragStartBehavior = DragStartBehavior.start,
    AppPrivateCommandCallback? onAppPrivateCommand,
    String? restorationId,
    ScrollController? scrollController,
    TextSelectionControls? selectionControls,
    ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.tight,
    ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
    TextEditingController? controller,
    Clip clipBehavior = Clip.hardEdge,
    bool enableIMEPersonalizedLearning = true,
    bool scribbleEnabled = true,
    // [beforeTap] 是 [onTap]前置操作，返回[true]时则执行接下来的默认操作，返回[false]，则拦截默认操作
    bool Function()? beforeTap,
    Function()? onChange,
    ReactiveFormFieldCallback<ModelDataType>? onTap,
    ReactiveFormFieldCallback<ModelDataType>? onEditingComplete,
    ReactiveFormFieldCallback<ModelDataType>? onSubmitted,
    SelectPageProp<ModelDataType>? selectPageProp,

    /// 录入项 值
    String Function(ViewDataType?)? value,
  })  : _textController = controller,
        super(
          key: key,
          formControl: formControl,
          formControlName: formControlName,
          valueAccessor: valueAccessor,
          validationMessages: validationMessages,
          showErrors: showErrors,
          builder: (ReactiveFormFieldState<ModelDataType, ViewDataType> field) {
            final state =
                field as ReactiveTextSelectState<ModelDataType, ViewDataType>;
            final effectiveDecoration = decoration
                .applyDefaults(Theme.of(state.context).inputDecorationTheme);

            state._setFocusNode(focusNode);

            var textfield = TextField(
              controller: state._textController
                ..text = value?.call(state.value) ?? state._textController.text,
              focusNode: state.focusNode,
              decoration: effectiveDecoration.copyWith(
                hintText: effectiveDecoration.hintText ?? ResKey.pleSelect.tr,
                contentPadding: EdgeInsets.zero,
                prefixIcon: FormWidgetUtils.getTitleWidget(
                  title,
                  width: titleWidth,
                  labelStyle: labelStyle ?? effectiveDecoration.labelStyle,
                ),
                suffixIcon: effectiveDecoration.suffixIcon ??
                    FormWidgetUtils.getRightArrowIcon(),
                suffixIconConstraints:
                    const BoxConstraints(maxWidth: 24, maxHeight: 40),
                border: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colour.dividerColor,
                  ),
                ),
                enabledBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colour.dividerColor,
                  ),
                ),
                focusedBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colour.dividerColor,
                  ),
                ),
                disabledBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colour.dividerColor,
                  ),
                ),
                errorBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colour.dividerColor,
                  ),
                ),
              ),
              keyboardType: keyboardType,
              textInputAction: textInputAction,
              style: style ?? TextStyles.f16,
              strutStyle: strutStyle,
              textAlign: textAlign,
              textAlignVertical: textAlignVertical,
              textDirection: textDirection,
              textCapitalization: textCapitalization,
              autofocus: autofocus,
              toolbarOptions: toolbarOptions,
              readOnly: true,
              showCursor: showCursor,
              obscureText: obscureText,
              autocorrect: autocorrect,
              smartDashesType: smartDashesType ??
                  (obscureText
                      ? SmartDashesType.disabled
                      : SmartDashesType.enabled),
              smartQuotesType: smartQuotesType ??
                  (obscureText
                      ? SmartQuotesType.disabled
                      : SmartQuotesType.enabled),
              enableSuggestions: enableSuggestions,
              maxLengthEnforcement: maxLengthEnforcement,
              maxLines: maxLines,
              minLines: minLines,
              expands: expands,
              maxLength: maxLength,
              inputFormatters: inputFormatters,
              enabled: field.control.enabled,
              cursorWidth: cursorWidth,
              cursorHeight: cursorHeight,
              cursorRadius: cursorRadius,
              cursorColor: cursorColor,
              scrollPadding: scrollPadding,
              scrollPhysics: scrollPhysics,
              keyboardAppearance: keyboardAppearance,
              enableInteractiveSelection: enableInteractiveSelection,
              buildCounter: buildCounter,
              autofillHints: autofillHints,
              mouseCursor: mouseCursor,
              obscuringCharacter: obscuringCharacter,
              dragStartBehavior: dragStartBehavior,
              onAppPrivateCommand: onAppPrivateCommand,
              restorationId: restorationId,
              scrollController: scrollController,
              selectionControls: selectionControls,
              selectionHeightStyle: selectionHeightStyle,
              selectionWidthStyle: selectionWidthStyle,
              clipBehavior: clipBehavior,
              enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
              scribbleEnabled: scribbleEnabled,
              onTap: () {
                if (beforeTap?.call() == false) {
                  return;
                }

                switch (selectPageProp?.pickType) {
                  case PickType.picker:
                    var list = selectPageProp!.list
                        .map((e) => selectPageProp.getNameKey(e))
                        .toList();
                    var initSelectValues =
                        selectPageProp.initSelectValues.isNotEmpty
                            ? selectPageProp.initSelectValues[0]
                            : null;
                    var selectedIndex = initSelectValues == null
                        ? 0
                        : selectPageProp.list.indexOf(initSelectValues);
                    BrnMultiDataPicker(
                      context: state.context,
                      delegate: Brn1RowDelegate(list,
                          firstSelectedIndex: selectedIndex),
                      title: sprintf(ResKey.pleaseEnterSentence.tr,
                          [selectPageProp.dataName]),
                      confirmClick: (index) {
                        var value =
                            selectPageProp.list[index[0]] as ViewDataType;
                        selectPageProp.initSelectValues = [
                          value as ModelDataType
                        ];
                        state.didChange(value);
                        if (onChange != null) {
                          onChange();
                        }
                      },
                    ).show();
                    break;
                  case PickType.page:
                    Get.to(
                      () => SelectPagePage<ViewDataType>(),
                      routeName: RouterPath.selectPage,
                      binding: SelectPageBinding<ViewDataType>(),
                      arguments: selectPageProp,
                    )?.then((value) {
                      if (value is ViewDataType) {
                        selectPageProp!.initSelectValues = [
                          value as ModelDataType
                        ];
                        state.didChange(value);
                        if (onChange != null) {
                          onChange();
                        }
                      }
                    });
                    break;
                  default:
                    onTap != null ? onTap(field.control) : null;
                }
              },
              onSubmitted: onSubmitted != null
                  ? (_) => onSubmitted(field.control)
                  : null,
              onEditingComplete: onEditingComplete != null
                  ? () => onEditingComplete.call(field.control)
                  : null,
            );
            return Container(
              height: 50,
              child: Row(
                children: [
                  FormWidgetUtils.getStar(requireStar),
                  Expanded(child: textfield)
                ],
              ),
            );
          },
        );

  @override
  ReactiveFormFieldState<ModelDataType, ViewDataType> createState() =>
      ReactiveTextSelectState<ModelDataType, ViewDataType>();
}

class ReactiveTextSelectState<ModelDataType, ViewDataType>
    extends ReactiveFormFieldState<ModelDataType, ViewDataType> {
  late TextEditingController _textController;
  late FocusController _focusController;
  FocusNode? _focusNode;

  FocusNode get focusNode => _focusNode ?? _focusController.focusNode;

  @override
  void initState() {
    super.initState();
    _initializeTextController();
  }

  @override
  void subscribeControl() {
    _registerFocusController(FocusController());
    super.subscribeControl();
  }

  @override
  void unsubscribeControl() {
    _unregisterFocusController();
    super.unsubscribeControl();
  }

  @override
  void onControlValueChanged(dynamic value) {
    final effectiveValue = (value == null) ? '' : value.toString();
    _textController.value = _textController.value.copyWith(
      text: effectiveValue,
      selection: TextSelection.collapsed(offset: effectiveValue.length),
      composing: TextRange.empty,
    );

    super.onControlValueChanged(value);
  }

  @override
  ControlValueAccessor<ModelDataType, ViewDataType> selectValueAccessor() {
    return super.selectValueAccessor();
  }

  void _registerFocusController(FocusController focusController) {
    _focusController = focusController;
    control.registerFocusController(focusController);
  }

  void _unregisterFocusController() {
    control.unregisterFocusController(_focusController);
    _focusController.dispose();
  }

  void _setFocusNode(FocusNode? focusNode) {
    if (_focusNode != focusNode) {
      _focusNode = focusNode;
      _unregisterFocusController();
      _registerFocusController(FocusController(focusNode: _focusNode));
    }
  }

  void _initializeTextController() {
    final initialValue = value;
    final currentWidget =
        widget as ReactiveTextSelect<ModelDataType, ViewDataType>;
    _textController = (currentWidget._textController != null)
        ? currentWidget._textController!
        : TextEditingController();
    _textController.text = initialValue == null ? '' : initialValue.toString();
  }
}
